{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- **ODR（One Definition Rule）例外**\n",
    "   被标记为 `inline` 的实体（函数、变量、命名空间等）允许在多个翻译单元中拥有相同定义，最终由链接器合并为一个。\n",
    "- **不等于“强制内联优化”**\n",
    "   `inline` 与编译器是否将函数体展开为机器指令无直接关系；优化器仍可自行决定是否真正“内联”。\n",
    "- **C++20 的语境**\n",
    "   在现代 C++ 中，`inline` 常用来**放宽 ODR 限制**，使得可以在头文件中定义函数/变量而无需单独的 `.cpp` 定义。自 C++17 起，引入了“内联变量（inline variables）”；C++20 则让 `consteval`/`constinit` 等新特性与 `inline` 可以互补使用。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`inline` 的常见用法\n",
    "\n",
    "| 用法类别                     | 常见语法                                       | 说明                                                         |\n",
    "| ---------------------------- | ---------------------------------------------- | ------------------------------------------------------------ |\n",
    "| 函数                         | `inline int add(int a, int b);`                | 允许函数定义放在头文件，无需另建 `.cpp`；也适用于类内定义、`constexpr` 函数、`consteval` 函数。 |\n",
    "| 变量                         | `inline constexpr double pi = 3.14159;`        | 现代 C++ 的推荐写法：常量/全局状态可以直接定义在头文件，避免“多重定义”链接错误。 |\n",
    "| 静态数据成员                 | `struct X { inline static int counter = 0; };` | C++17 起允许类内直接定义静态成员；无需额外的外部定义。       |\n",
    "| 命名空间                     | `inline namespace v2 { ... }`                  | 主要用于版本控制，自动引入命名空间中的符号；可与模块或库版本策略配合。 |\n",
    "| `constexpr`/`consteval` 组合 | `inline consteval int magic()`                 | `constexpr` 与 `consteval` 函数隐式具备内联特性，将其显式写出可强调 ODR 语义。 |\n",
    "| 模板                         | 模板函数/变量默认“隐式内联”                    | 模板在头文件实现时可重复实例化；若对非模板实体需要同样的行为，则显式使用 `inline`。 |\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "头文件中的 `inline` 函数与变量\n",
    "- [math_utils.hpp](./math_utils.hpp)\n",
    "- [main.cpp](./main.cpp)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "vscode": {
     "languageId": "shellscript"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "=== inline 函数 ===\n",
      "add(3, 4) = 7\n",
      "\n",
      "=== inline constexpr 变量 ===\n",
      "kPi = 3.14159\n",
      "\n",
      "=== inline constinit 变量 ===\n",
      "g_init_calls (before) = 0\n",
      "g_init_calls (after) = 1\n",
      "\n",
      "=== inline 函数模板 ===\n",
      "sum(values) = 15\n",
      "\n",
      "=== inline static 成员 ===\n",
      "Stats::object_count = 2\n"
     ]
    }
   ],
   "source": [
    "! g++ -std=c++20 -O2 main.cpp -o demo && ./demo"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "类内 `inline static` 数据成员\n",
    "- 省去了在 `.cpp` 中写定义的麻烦。\n",
    "- 可放在头文件中被多个翻译单元使用而不引起 ODR 冲突。\n",
    "- 可与 `constexpr`、`constinit` 等组合，提升初始化安全性。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "cpp"
    }
   },
   "outputs": [],
   "source": [
    "// 在 C++17 之前，类的静态成员必须**在类外**提供一次定义：\n",
    "\n",
    "struct Foo {\n",
    "  static int count;\n",
    "};\n",
    "int Foo::count = 0; // 旧写法"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "cpp"
    }
   },
   "outputs": [],
   "source": [
    "// 自 C++17 起（在 C++20 中自然可用）可以直接写：\n",
    "\n",
    "struct Foo {\n",
    "  inline static int count = 0;\n",
    "};"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`inline namespace` 在库版本中的应用示例\n",
    "- `inline namespace` 的成员会自动提升到父命名空间，使得库可以提供多个版本而不影响默认使用者。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "vscode": {
     "languageId": "cpp"
    }
   },
   "outputs": [],
   "source": [
    "namespace mylib {\n",
    "inline namespace v2 { // 默认版本\n",
    "  inline int answer() { return 42; }\n",
    "}\n",
    "namespace v1 {\n",
    "  inline int answer() { return 41; }\n",
    "}\n",
    "} // namespace mylib"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "vscode": {
     "languageId": "cpp"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "42\n",
      "41\n"
     ]
    }
   ],
   "source": [
    "#include <iostream>\n",
    "\n",
    "{\n",
    "  std::cout << mylib::answer() << '\\n';      // v2::answer\n",
    "  std::cout << mylib::v1::answer() << '\\n';  // 仍可访问旧版本\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "使用 `inline` 的最佳实践\n",
    "\n",
    "1. **表达 ODR 意图而非性能目标**：若想影响调用开销，应使用优化选项或 `[[gnu::always_inline]]` 等属性，而非纯 `inline`。\n",
    "2. **头文件定义策略**：凡是需要在头文件定义的非模板函数/变量，优先加上 `inline`。\n",
    "3. **结合现代关键字**：\n",
    "   - `inline constexpr`、`inline consteval`、`inline constinit` 能明确表达编译期意图。\n",
    "   - `inline static` 消除类内静态成员的额外定义。\n",
    "4. **保持定义一致**：所有翻译单元看到的 `inline` 定义必须逐字相同，否则行为未定义。\n",
    "5. **与模块搭配**：在 C++20 模块中，`inline` 仍用于导出的函数/变量以允许多次导入；不过模块本身在一定程度上缓解了 ODR 的头疼问题。"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "C++17",
   "language": "C++17",
   "name": "xcpp17"
  },
  "language_info": {
   "codemirror_mode": "text/x-c++src",
   "file_extension": ".cpp",
   "mimetype": "text/x-c++src",
   "name": "c++",
   "version": "17"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
